import os
import time
import threading
from collections import deque
from typing import Deque, Dict, List, Optional, Tuple, Any

# ---------------- ReliableMQ ----------------
class ReliableMQ:
    """
    简单可靠 MQ：
    - 每条消息 (id, ts, line)
    - 保留最近 max_size 条，自动基于最小未读 id 回收
    - subscriber_id 可以是任意 hashable（比如 websocket id）
    """
    def __init__(self, max_size: int = 10000):
        self._lock = threading.Lock()
        self._queue: Deque[Tuple[int, float, str]] = deque()
        self._next_id = 1
        self._max_size = max_size
        self._subscribers: Dict[Any, int] = {}  # subscriber -> last_read_id (exclusive)

    def set(self, line: str, app_id: Optional[str] = None, ts: Optional[float] = None) -> int:
        """推送一条消息，返回 msg_id"""
        with self._lock:
            msg_id = self._next_id
            self._next_id += 1
            if ts is None:
                ts = time.time()
            self._queue.append((msg_id, ts, line))
            # 如果超限，基于最小未读的 subscriber 自动回收（保守）
            self._ensure_capacity()
            return msg_id

    def _ensure_capacity(self):
        # 计算可安全丢弃到的最小 unacked id
        if not self._queue:
            return
        if len(self._queue) <= self._max_size:
            return
        # 找到所有订阅者最小 last_read_id，不能丢小于等于它的消息
        if self._subscribers:
            min_unacked = min(self._subscribers.values())
        else:
            # 没有订阅者，直接保留末尾 max_size
            min_unacked = self._queue[-1][0]  # last id
        # 丢弃直到满足大小或到达 min_unacked
        while len(self._queue) > self._max_size:
            if self._queue and self._queue[0][0] < min_unacked:
                self._queue.popleft()
            else:
                # 无法丢弃更多（必须保留以供未读者消费）
                break

    def get(self, subscriber_id: Any) -> List[Tuple[int, float, str]]:
        """返回自上次读取后的所有消息（msg_id, ts, line）"""
        with self._lock:
            last = self._subscribers.get(subscriber_id, 0)
            # 找到第一个 id > last
            res = []
            for msg_id, ts, line in self._queue:
                if msg_id > last:
                    res.append((msg_id, ts, line))
            if res:
                # 更新 subscriber 的 last_read 为最后一条 msg_id
                self._subscribers[subscriber_id] = res[-1][0]
            else:
                # 保证 subscriber 存在（避免被误当作未曾订阅）
                if subscriber_id not in self._subscribers:
                    # 初始化为当前最后一条 id（这样新订阅者开始时不重复读全部历史）
                    self._subscribers[subscriber_id] = self._queue[-1][0] if self._queue else 0
            # 触发容量检查（可能有人新订阅）
            self._ensure_capacity()
            return res

    def unsubscribe(self, subscriber_id: Any):
        with self._lock:
            self._subscribers.pop(subscriber_id, None)
            # 不清空全局队列 — 这样不会影响其他订阅者
